import { pushTarget, popTarget } from './dep'
import { queueWatcher } from './scheduler'
import { parsePath , noop} from '../../util/index'

let watcherId = 0
export class Watcher {
    constructor(vm, expOrFn, cb, options, isRenderWatcher) {
        if (isRenderWatcher) {
            vm._watcher = this
        }
        vm._watchers.push(this)
        if (options) {
            this.lazy = !!options.lazy
            this.sync = !!options.sync
            this.before = options.before
            this.user = !!options.user
        } else {
            this.lazy = false
        }
        this.dirty = this.lazy
        this.vm = vm
        this.cb = cb
        this.depIds = new Set()
        this.deps = []
        this.id = ++watcherId
        this.active = true
        if (typeof expOrFn === 'function') {
            this.getter = expOrFn
        } else {
            //表达式
            this.getter = parsePath(expOrFn)
            if (!this.getter) {
              this.getter = noop
            }
        }
        this.value = this.lazy ? undefined : this.get()
    }
    get() {
        pushTarget(this)
        let value = this.getter.call(this.vm, this.vm) //传递参数是为了实现watcher
        popTarget()
        this.cleanupDeps()
        return value
    }
    evaluate() {
        this.value = this.get()
        this.dirty = false
    }
    addDep(dep) {
        const id = dep.id
        if (!this.depIds.has(id)) {
            this.deps.push(dep)
            this.depIds.add(id)
            dep.addSub(this)
        }
    }
    update() {
        if (this.lazy) {
            this.dirty = true
        } else if (this.sync) {
            this.run()
        } else {
            queueWatcher(this)
        }
    }
    run() {
        if (this.active) {
            const value = this.get()
            const oldValue = this.value
            this.value = value
            this.cb && this.cb.call(this.vm, value, oldValue)
        }
    }
    depend() { //通知收集了当前wathcer的dep收集Dep.target正在运行的watcher
        let i = this.deps.length
        while (i--) {
            this.deps[i].depend()
        }
    }
    //去重
    cleanupDeps() {
        let i = this.deps.length
        while (i--) {
            const dep = this.deps[i]
            if (!this.depIds.has(dep.id)) {
                dep.removeSub(this)
            }
        }
    }
}